package com.hou.mytomcat.catalina;

import cn.hutool.log.LogFactory;
import com.hou.mytomcat.http.Request;
import com.hou.mytomcat.http.Response;
import com.hou.mytomcat.util.ThreadPoolUtil;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

/**
 * 管理NIO的连接、业务
 */
public class ConnectorNIO extends BaseConnector implements Runnable {

    public ConnectorNIO(Service service) {
        super(service);
    }

    @Override
    public void run() {
        Selector selector = null;
        ServerSocketChannel serverSocketChannel = null;
        try {
            //监听特定端口，用来接收客户端请求
            serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.socket().bind(new InetSocketAddress(port));
            serverSocketChannel.configureBlocking(false);
            selector = Selector.open();
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            //单线程接收处理请求
            while (true) {
                int readyChannels = selector.select();
                if(readyChannels == 0)
                    continue;
                //已经就绪的通道
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();

                if(selectionKeys.isEmpty()) {
                    continue;
                }
                while(iterator.hasNext()) {

                    SelectionKey key = iterator.next();
                    if(!key.isValid()) {
                        continue;
                    }

                    if (key.isAcceptable()) {
                        ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
                        SocketChannel clientChannel = serverChannel.accept();
                        clientChannel.configureBlocking(false);
                        clientChannel.register(key.selector(), SelectionKey.OP_READ);
                    } else if (key.isReadable()) {
                        //读
                        SocketChannel clientChannelNIO = (SocketChannel) key.channel();
                        clientChannelNIO.configureBlocking(false);
                        byte[] httpInfo = readByteFromChannel(clientChannelNIO, key);

                        //业务处理
                        ThreadPoolUtil.run(new Runnable() {
                            @Override
                            public void run() {
                                try {
                                    String requestString = new String(httpInfo);
                                    Request request = new Request( requestString, ConnectorNIO.this);
                                    com.hou.mytomcat.http.Response response = new Response();
                                    HttpProcessor processor = new HttpProcessor();
                                    byte[] reply = processor.execute(request, response);
                                    doWrite(clientChannelNIO, reply);
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }
                            }
                        });
                    }
                    iterator.remove();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if(selector != null) {
                    selector.close();
                }
                if(serverSocketChannel != null){
                    serverSocketChannel.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public void init() {
        LogFactory.get().info("Initializing ProtocolHandler [http-nio-{}]", port);
    }

    //根据端口数量创建对应的线程监听
    public void start() {
        LogFactory.get().info("Starting ProtocolHandler [http-nio-{}]", port);
        new Thread(this).start();
    }

    public byte[] readByteFromChannel(SocketChannel clientChannelNIO, SelectionKey key) throws Exception {
        int buffer_size = 1024;
        ByteBuffer bufferNIO = ByteBuffer.wrap(new byte[buffer_size]);

        List<Byte> result = new ArrayList<>();
        int bytesRead = clientChannelNIO.read(bufferNIO);
        while(bytesRead > 0) {
            bufferNIO.flip();
            while (bufferNIO.hasRemaining()) {
                result.add(bufferNIO.get());
            }
            bufferNIO.clear();
            bytesRead = clientChannelNIO.read(bufferNIO);
        }
        if(bytesRead == -1) {
            clientChannelNIO.close();
        }
        byte[] bytes = new byte[result.size()];
        for(int i = 0; i < result.size(); i ++) {
            bytes[i] = result.get(i);
        }
        return bytes;
    }

    public void doWrite(SocketChannel socketChannel, byte[] reply) throws IOException {
        int buffer_size = 1024;
        ByteBuffer bufferNIOWrite = ByteBuffer.wrap(new byte[buffer_size]);
        bufferNIOWrite.put(reply);
        while(bufferNIOWrite.hasRemaining()) {
            bufferNIOWrite.flip();
            socketChannel.write(bufferNIOWrite);
        }
        if (socketChannel != null) {
            socketChannel.close();
        }
    }
}
